#!/bin/sh
# Minimal Object-Oriented style PreProcessor.

# Copyright (C) 2006-2008, 2016, 2018-2019 Free Software Foundation, Inc.
# Written by Bruno Haible <bruno@clisp.org>, 2006.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.

# Usage: moopp source.oo.c source.oo.h superclass.oo.h ...
# Arguments:
#   - the source file of the class,
#   - the header file declaring the class,
#   - the header file declaring its superclass,
#   - etc. up to the root class.
# Creates four files in the current directory:
#   - source.c, the preprocessing result of source.oo.c,
#   - source.h, the preprocessing result of source.oo.h,
#   - class.priv.h, a file declaring the private fields of the class,
#   - class.vt.h, a file declaring the virtual function table of the class.

# This implementation of the preprocessor is a quick hack. It makes assumptions
# about the source code:
#   - GNU indentation style,
#   - the struct declaration must be in a single line,
#   - no comments on the struct declaration line,
#   - no #ifs in relevant position,
#   - ...
# Someday this should be rewritten to use a proper tokenizer and parser.

# func_usage
# outputs to stdout the --help usage message.
func_usage ()
{
  echo "\
Usage: moopp [OPTION]... SOURCE.oo.c SOURCE.oo.h SUPERCLASS.oo.h ...

Preprocesses SOURCE.oo.c into CLASS.c and SOURCE.oo.h into CLASS.h,
where CLASS is the name of the class defined in these files.

See moo.h for the object-oriented features and the syntax of the input files.

Options:
      --help           print this help and exit
      --version        print version information and exit
      --dllexport=NAME Arrange so that the specified class name can be accessed
                       from outside the shared library it is compiled into.
                       This option can be repeated.

Report bugs to <bruno@clisp.org>."
}

# func_version
# outputs to stdout the --version message.
func_version ()
{
  echo "$progname (GNU $package) $version"
  echo "Copyright (C) 2006-2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law."
  echo "Written by" "Bruno Haible"
}

# func_fatal_error message
# outputs to stderr a fatal error message, and terminates the program.
func_fatal_error ()
{
  echo "moopp: *** $1" 1>&2
  echo "moopp: *** Stop." 1>&2
  exit 1
}

# Command-line option processing.
# Removes the OPTIONS from the arguments. Sets the variables:
# - dllexports      list of class names to export from Woe32 DLLs
dllexports=
while test $# -gt 0; do
  case "$1" in
    --dllexport | --dllexpor | --dllexpo | --dllexp | --dllex | --dlle )
      shift
      if test $# = 0; then
        func_fatal_error "missing argument for --dllexport"
      fi
      case "$1" in
        -*) func_fatal_error "missing argument for --dllexport" ;;
      esac
      dllexports="$dllexports $1"
      shift ;;
    --dllexport=* )
      arg=`echo "X$1" | sed -e 's/^X--dllexport=//'`
      dllexports="$dllexports $arg"
      shift ;;
    --help | --hel | --he | --h )
      func_usage
      exit 0 ;;
   --version | --versio | --versi | --vers | --ver | --ve | --v )
      func_version
      exit 0 ;;
    -- )      # Stop option prcessing
      shift; break ;;
    -* )
      func_fatal_error "unrecognized option: $option"
      ;;
    * )
      break ;;
  esac
done

if test $# -lt 2; then
  func_fatal_error "Usage: $0 [OPTION]... source.oo.c source.oo.h superclass.oo.h ..."
fi

# Check that all files exist.
for file
do
  test -r "$file" || {
    func_fatal_error "file $file does not exist"
  }
done

source_impl_file="$1"
source_header_file="$2"
shift
shift

case "$source_impl_file" in
  *.oo.c) ;;
  *) func_fatal_error "invalid class source file name: $source_impl_file" ;;
esac
case "$source_header_file" in
  *.oo.h) ;;
  *) func_fatal_error "invalid class header file name: $source_header_file" ;;
esac

# A sed expression that removes empty lines.
sed_remove_empty_lines='/^$/d'

# A sed expression that removes ANSI C and ISO C99 comments.
sed_remove_comments="
/[/][/*]/{
  ta
  :a
  s,^\\(\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*\\)//.*,\\1,
  te
  s,^\\(\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*\\)/[*]\\([^*]\\|[*][^/*]\\)*[*][*]*/,\\1 ,
  ta
  /^\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*[/][*]/{
    s,^\\(\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*\\)/[*].*,\\1 ,
    tu
    :u
    n
    s,^\\([^*]\\|[*][^/*]\\)*[*][*]*/,,
    tv
    s,^.*\$,,
    bu
    :v
  }
  :e
}"
# The same thing as an extended regular expression, for use with
# sed --regexp-extended.
sed_remove_comments_ERE="
/[/][/*]/{
  ta
  :a
  s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)//.*,\\1,
  te
  s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)/[*]([^*]|[*][^/*])*[*][*]*/,\\1 ,
  ta
  /^([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*[/][*]/{
    s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)/[*].*,\\1 ,
    tu
    :u
    n
    s,^([^*]|[*][^/*])*[*][*]*/,,
    tv
    s,^.*\$,,
    bu
    :v
  }
  :e
}"

# Check that 'sed' supports the kind of regular expressions used in
# sed_remove_comments. The use of \| meaning alternation of basic regular
# expressions is a GNU extension.
sed_test='s,^\(\(a\|X\)*\)//.*,\1,'
sed_result=`echo 'aaa//bcd' | sed -e "$sed_test"`
test "$sed_result" = 'aaa' \
  || func_fatal_error "The 'sed' program is not GNU sed. Try installing GNU sed."

# func_check_impl_syntax file
# Check the syntax of the source implementation file.
# Output:
#   - classname         name of the class being defined (without 'struct')
#   - superclassname    name of the superclass, or empty for a root class
#   - impl_decl_lineno  line number of the class name declaration ('struct')
#   - impl_beg_lineno   line number of the start of the class declaration ('{')
#   - impl_end_lineno   line number of the end of the class declaration ('}')
#   - fields            field declarations, including preprocessor directives
func_check_impl_syntax ()
{
  file="$1"
  sed -e "$sed_remove_comments" < "$file" | grep '^fields:' > /dev/null || {
    func_fatal_error "$file does not contain 'fields:'"
  }
  test `sed -e "$sed_remove_comments" < "$file" | grep -c '^fields:'` = 1 || {
    func_fatal_error "$file contains more than one 'fields:'"
  }
  fields_lineno=`sed -e "$sed_remove_comments" < "$file" | grep -n '^fields:' | sed -e 's,:.*,,'`
  sed_before_fields="$fields_lineno"',$d'
  impl_decl_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_fields" | grep -n '^struct[ 	]' | tail -n 1 | sed -e 's,:.*,,'`
  test -n "$impl_decl_lineno" || {
    func_fatal_error "$file: class declaration not found"
  }
  class_line=`sed -e "$sed_remove_comments" < "$file" | sed -n -e "$impl_decl_lineno"'p'`
  sed_extract_classname='s,^struct[ 	][ 	]*\([A-Za-z_0-9]*\).*,\1,p'
  classname=`echo "$class_line" | sed -n -e "$sed_extract_classname"`
  test -n "$classname" || {
    func_fatal_error "$0: $file: class name not recognized at line $impl_decl_lineno"
  }
  superclassname=
  if echo "$class_line" | grep ':' > /dev/null; then
    sed_extract_superclassname='s,^.*:[ 	]*struct[ 	][ 	]*\([A-Za-z_0-9]*\).*,\1,p'
    superclassname=`echo "$class_line" | sed -n -e "$sed_extract_superclassname"`
    test -n "$superclassname" || {
      func_fatal_error "$file: superclass name not recognized at line $impl_decl_lineno"
    }
  fi
  impl_beg_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_fields" | grep -n '^{' | tail -n 1 | sed -e 's,:.*,,'`
  { test -n "$impl_beg_lineno" && test "$impl_decl_lineno" -lt "$impl_beg_lineno"; } || {
    func_fatal_error "$file: opening brace of class declaration not found after line $impl_decl_lineno"
  }
  sed_after_fields='1,'"$fields_lineno"'d'
  impl_end_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_after_fields" | grep -n '^}' | sed -e '1q' | sed -e 's,:.*,,'`
  test -n "$impl_end_lineno" || {
    func_fatal_error "$file: closing brace of class declaration not found after line $fields_lineno"
  }
  impl_end_lineno=`expr $fields_lineno + $impl_end_lineno`
  sed_extract_fields="$impl_end_lineno"',$d;1,'"$fields_lineno"'d'
  fields=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_extract_fields"`
}

# func_check_header_syntax file
# Check the syntax of a header file.
# Output:
#   - classname         name of the class being defined (without 'struct')
#   - superclassname    name of the superclass, or empty for a root class
#   - class_decl_lineno line number of the class name declaration ('struct')
#   - class_beg_lineno  line number of the start of the class declaration ('{')
#   - class_end_lineno  line number of the end of the class declaration ('}')
#   - methods           newline-separated list of method declarations
func_check_header_syntax ()
{
  file="$1"
  sed -e "$sed_remove_comments" < "$file" | grep '^methods:' > /dev/null || {
    func_fatal_error "$file does not contain 'methods:'"
  }
  test `sed -e "$sed_remove_comments" < "$file" | grep -c '^methods:'` = 1 || {
    func_fatal_error "$file contains more than one 'methods:'"
  }
  methods_lineno=`sed -e "$sed_remove_comments" < "$file" | grep -n '^methods:' | sed -e 's,:.*,,'`
  sed_before_methods="$methods_lineno"',$d'
  class_decl_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_methods" | grep -n '^struct[ 	]' | tail -n 1 | sed -e 's,:.*,,'`
  test -n "$class_decl_lineno" || {
    func_fatal_error "$file: class declaration not found"
  }
  class_line=`sed -e "$sed_remove_comments" < "$file" | sed -n -e "$class_decl_lineno"'p'`
  sed_extract_classname='s,^struct[ 	][ 	]*\([A-Za-z_0-9]*\).*,\1,p'
  classname=`echo "$class_line" | sed -n -e "$sed_extract_classname"`
  test -n "$classname" || {
    func_fatal_error "$0: $file: class name not recognized at line $class_decl_lineno"
  }
  superclassname=
  if echo "$class_line" | grep ':' > /dev/null; then
    sed_extract_superclassname='s,^.*:[ 	]*struct[ 	][ 	]*\([A-Za-z_0-9]*\).*,\1,p'
    superclassname=`echo "$class_line" | sed -n -e "$sed_extract_superclassname"`
    test -n "$superclassname" || {
      func_fatal_error "$file: superclass name not recognized at line $class_decl_lineno"
    }
  fi
  class_beg_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_methods" | grep -n '^{' | tail -n 1 | sed -e 's,:.*,,'`
  { test -n "$class_beg_lineno" && test "$class_decl_lineno" -lt "$class_beg_lineno"; } || {
    func_fatal_error "$file: opening brace of class declaration not found after line $class_decl_lineno"
  }
  sed_after_methods='1,'"$methods_lineno"'d'
  class_end_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_after_methods" | grep -n '^}' | sed -e '1q' | sed -e 's,:.*,,'`
  test -n "$class_end_lineno" || {
    func_fatal_error "$file: closing brace of class declaration not found after line $methods_lineno"
  }
  class_end_lineno=`expr $methods_lineno + $class_end_lineno`
  sed_extract_methods="$class_end_lineno"',$d;1,'"$methods_lineno"'d'
  methods=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_extract_methods" | tr '\015\n' '  ' | tr ';' '\n' | sed -e 's,[ 	]*$,,'`
  sed_remove_valid_arg1_lines='/([ 	]*'"$classname"'_t[ 	]*[A-Za-z_0-9]*[ 	]*[,)]/d'
  sed_extract_method_name='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)[ 	]*(.*$,\1,'
  methods_with_bad_arg1=`echo "$methods" | sed -e "$sed_remove_empty_lines" -e "$sed_remove_valid_arg1_lines" -e "$sed_extract_method_name"`
  if test -n "$methods_with_bad_arg1"; then
    methods_with_bad_arg1=`{ echo "$methods_with_bad_arg1" | sed -e 's/$/, /' | tr -d '\015\n'; echo; } | sed -e 's/\(, \)*$//'`
    func_fatal_error "$file: some methods don't have a first argument of type ${classname}_t: $methods_with_bad_arg1"
  fi
}

func_check_impl_syntax "$source_impl_file"
impl_classname="$classname"
impl_superclassname="$superclassname"

func_check_header_syntax "$source_header_file"
main_classname="$classname"
main_superclassname="$superclassname"
main_class_decl_lineno="$class_decl_lineno"
main_class_beg_lineno="$class_beg_lineno"
main_class_end_lineno="$class_end_lineno"
main_methods="$methods"
all_superclasses=
all_methods="$methods"
inherited_methods=
last_header_file="$source_header_file"
expected_superclassname="$superclassname"

for file
do
  if test -z "$expected_superclassname"; then
    func_fatal_error "file $last_header_file does not specify a superclass; superfluous command line argument $file"
  fi
  func_check_header_syntax "$file"
  all_superclasses="$classname $all_superclasses"
  all_methods="$methods
$all_methods"
  inherited_methods="$methods
$inherited_methods"
  if test "$classname" != "$expected_superclassname"; then
    func_fatal_error "file $last_header_file specifies superclass '$expected_superclassname', but file $file defines class '$classname'"
  fi
  last_header_file="$file"
  expected_superclassname="$superclassname"
done

if test -n "$expected_superclassname"; then
  func_fatal_error "$0: file $last_header_file specifies superclass '$expected_superclassname', please specify the header file that defines it as additional command line argument"
fi

if test "$impl_classname" != "$main_classname"; then
  func_fatal_error "file $source_header_file specifies class '$main_classname', but file $source_impl_file specifies class '$impl_classname'"
fi
if test "$impl_superclassname" != "$main_superclassname"; then
  if test -z "$main_superclassname"; then
    func_fatal_error "file $source_header_file specifies no superclass, but file $source_impl_file specifies a superclass '$impl_superclassname'"
  fi
  if test -z "$impl_superclassname"; then
    func_fatal_error "file $source_header_file specifies a superclass '$main_superclassname', but file $source_impl_file specifies no superclass"
  fi
  func_fatal_error "file $source_header_file specifies a superclass '$main_superclassname', but file $source_impl_file specifies a superclass '$impl_superclassname'"
fi

# func_start_creation file
# starts the creation of the named file.
func_start_creation ()
{
  file="$1"
  if test -f "$file"; then
    echo "Updating $file (backup in ${file}~)"
    mv -f "$file" "${file}~" || func_fatal_error "failed"
  else
    echo "Creating $file"
  fi
}

# func_emit_priv_h newfile
# outputs to $newfile the contents of class.priv.h.
func_emit_priv_h ()
{
  newfile="$1"
  {
    echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
    echo
    if test -n "${main_superclassname}"; then
      echo "/* Field layout of superclass.  */"
      echo "#include \"${main_superclassname}.priv.h\""
      echo
    fi
    echo "/* Field layout of ${main_classname} class.  */"
    echo "struct ${main_classname}_representation"
    echo "{"
    if test -n "${main_superclassname}"; then
      echo "  struct ${main_superclassname}_representation base;"
    else
      echo "  const void *vtable;"
    fi
    echo "$fields" | sed -e "$sed_remove_empty_lines"
    echo "};"
  } > "$newfile"
}

# func_emit_vt_h newfile
# outputs to $newfile the contents of class.vt.h.
func_emit_vt_h ()
{
  newfile="$1"
  {
    echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
    echo
    if test -n "${main_superclassname}"; then
      echo "/* Virtual function table layout of superclass.  */"
      echo "#include \"${main_superclassname}.vt.h\""
      echo
    fi
    echo "/* Virtual function table layout of ${main_classname} class.  */"
    echo "$main_methods" | sed -e "$sed_remove_empty_lines" -e 's/\([^A-Za-z_0-9]\)\([A-Za-z_0-9][A-Za-z_0-9]*\)[ 	]*([^,)]*/\1(*\2) (THIS_ARG/' -e 's,$,;,'
  } > "$newfile"
}

# In C++ mode, we have a precise type checking. But in C mode, we have only
# loose type checking: So that rootclass_t and subclass_t are assignment
# compatible, we have to define subclass_t as identical to rootclass_t.
# Therefore we need an alias name for the representation of any type in the
# hierarchy.
if test -z "$main_superclassname"; then
  main_repclassalias="any_${main_classname}_representation"
else
  main_repclassalias="${main_classname}_representation"
fi

sed_extract_method_rettype='s,^\(.*[^A-Za-z_0-9]\)[A-Za-z_0-9][A-Za-z_0-9]*[ 	]*(.*$,\1,
s,^[ 	]*,,
s,[ 	]*$,,'
sed_extract_method_name='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)[ 	]*(.*$,\1,'
sed_extract_method_arglist='s,^.*[^A-Za-z_0-9][A-Za-z_0-9][A-Za-z_0-9]*[ 	]*([^,)]*\(.*\)).*$,'"${main_classname}_t"' first_arg\1,'
sed_extract_method_args='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)$,\1,'

# func_emit_source_h newfile newfile_base
# outputs to $newfile the contents of source.h.
source_header_file_base=`echo "$source_header_file" | sed -e 's,^.*/,,'`
func_emit_source_h ()
{
  newfile="$1"
  newfile_base="$2"
  # Use DLL_VARIABLE if and only if the main classname is among the names
  # specified with --dllexport options.
  dllexport_for_variables=
  for name in $dllexports; do
    if test "${main_classname}" = "${name}"; then
      dllexport_for_variables=" DLL_VARIABLE"
      break
    fi
  done
  {
    echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
    echo
    echo "#line 1 \"${source_header_file_base}\""
    cat "$source_header_file" | sed -e "${main_class_decl_lineno}"',$d'
    echo "#line "`expr 3 + ${main_class_decl_lineno} + 1`" \"$newfile_base\""
    echo "struct ${main_repclassalias};"
    echo "/* ${main_classname}_t is defined as a pointer to struct ${main_repclassalias}."
    echo "   In C++ mode, we use a smart pointer class."
    echo "   In C mode, we have no other choice than a typedef to the root class type.  */"
    echo "#if IS_CPLUSPLUS"
    echo "struct ${main_classname}_t"
    echo "{"
    echo "private:"
    echo "  struct ${main_repclassalias} *_pointer;"
    echo "public:"
    echo "  ${main_classname}_t () : _pointer (NULL) {}"
    echo "  ${main_classname}_t (struct ${main_repclassalias} *pointer) : _pointer (pointer) {}"
    echo "  struct ${main_repclassalias} * operator -> () { return _pointer; }"
    echo "  operator struct ${main_repclassalias} * () { return _pointer; }"
    atroot=yes
    for classname in $all_superclasses; do
      if test -n "$atroot"; then
        repclassalias="any_${classname}_representation"
      else
        repclassalias="${classname}_representation"
      fi
      echo "  operator struct ${repclassalias} * () { return (struct ${repclassalias} *) _pointer; }"
      atroot=
    done
    # The 'operator void *' is needed to avoid ambiguous conversion chains.
    echo "  operator void * () { return _pointer; }"
    # The 'operator ==' and 'operator !=' are needed to avoid ambiguous comparisons with NULL.
    echo "  bool operator == (const void *p) { return _pointer == p; }"
    echo "  bool operator != (const void *p) { return _pointer != p; }"
    atroot=yes
    for classname in $all_superclasses; do
      if test -n "$atroot"; then
        repclassalias="any_${classname}_representation"
      else
        repclassalias="${classname}_representation"
      fi
      echo "  operator ${classname}_t () { return (${classname}_t) (struct ${repclassalias} *) _pointer; }"
      # The 'explicit' constructors allow to downcast.
      echo "  explicit ${main_classname}_t (${classname}_t x) : _pointer ((struct ${main_repclassalias} *) (void *) x) {}"
      atroot=
    done
    echo "};"
    echo "#else"
    if test -n "${main_superclassname}"; then
      echo "typedef ${main_superclassname}_t ${main_classname}_t;"
    else
      echo "typedef struct ${main_repclassalias} * ${main_classname}_t;"
    fi
    echo "#endif"
    echo
    echo "/* Functions that invoke the methods.  */"
    echo "#ifdef __cplusplus"
    echo "extern \"C\" {"
    echo "#endif"
    echo "$all_methods" | sed -e "$sed_remove_empty_lines" -e 's/\([^A-Za-z_0-9]\)\([A-Za-z_0-9][A-Za-z_0-9]*\)[ 	]*([^,)]*/\1'"${main_classname}_"'\2 ('"${main_classname}_t first_arg"'/' -e 's,^,extern ,' -e 's,$,;,'
    echo "#ifdef __cplusplus"
    echo "}"
    echo "#endif"
    echo
    # Now come the implementation details.
    echo "/* Type representing an implementation of ${main_classname}_t.  */"
    echo "struct ${main_classname}_implementation"
    echo "{"
    echo "  const typeinfo_t * const *superclasses;"
    echo "  size_t superclasses_length;"
    echo "  size_t instance_size;"
    echo "#define THIS_ARG ${main_classname}_t first_arg"
    echo "#include \"${main_classname}.vt.h\""
    echo "#undef THIS_ARG"
    echo "};"
    echo
    echo "/* Public portion of the object pointed to by a ${main_classname}_t.  */"
    echo "struct ${main_classname}_representation_header"
    echo "{"
    echo "  const struct ${main_classname}_implementation *vtable;"
    echo "};"
    echo
    echo "#if HAVE_INLINE"
    echo
    echo "/* Define the functions that invoke the methods as inline accesses to"
    echo "   the ${main_classname}_implementation."
    echo "   Use #define to avoid a warning because of extern vs. static.  */"
    echo
    echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
      while read method; do
        rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
        name=`echo "$method" | sed -e "$sed_extract_method_name"`
        arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
        if test "$arglist" = "void"; then
          args=
        else
          args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
        fi
        if test "$rettype" = "void"; then
          return=
        else
          return="return "
        fi
        echo "# define ${main_classname}_${name} ${main_classname}_${name}_inline"
        echo "static inline $rettype"
        echo "${main_classname}_${name} ($arglist)"
        echo "{"
        echo "  const struct ${main_classname}_implementation *vtable ="
        echo "    ((struct ${main_classname}_representation_header *) (struct ${main_repclassalias} *) first_arg)->vtable;"
        echo "  ${return}vtable->${name} ($args);"
        echo "}"
        echo
      done
    echo "#endif"
    echo
    echo "extern${dllexport_for_variables} const typeinfo_t ${main_classname}_typeinfo;"
    if test -n "${main_superclassname}"; then
      superclasses_array_initializer="${main_superclassname}_SUPERCLASSES"
    else
      superclasses_array_initializer="NULL"
    fi
    echo "#define ${main_classname}_SUPERCLASSES &${main_classname}_typeinfo, ${superclasses_array_initializer}"
    if test -n "${main_superclassname}"; then
      echo "#define ${main_classname}_SUPERCLASSES_LENGTH (1 + ${main_superclassname}_SUPERCLASSES_LENGTH)"
    else
      echo "#define ${main_classname}_SUPERCLASSES_LENGTH (1 + 1)"
    fi
    echo
    echo "extern${dllexport_for_variables} const struct ${main_classname}_implementation ${main_classname}_vtable;"
    echo
    echo "#line "`expr $main_class_end_lineno + 1`" \"${source_header_file_base}\""
    cat "$source_header_file" | sed -e "1,${main_class_end_lineno}d"
  } > "$newfile"
}

# func_emit_source_c newfile newfile_base
# outputs to $newfile the contents of source.c.
source_impl_file_base=`echo "$source_impl_file" | sed -e 's,^.*/,,'`
func_emit_source_c ()
{
  newfile="$1"
  newfile_base="$2"
  {
    echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
    echo
    # In C mode, where subclass_t is identical to rootclass_t, we define the
    # any_rootclass_representation type to the right one for subclass.
    if test -n "$all_superclasses"; then
      for classname in $all_superclasses; do
        rootclassname="$classname"
        break
      done
    else
      rootclassname="$main_classname"
    fi
    echo "#if !IS_CPLUSPLUS"
    echo "#define ${main_classname}_representation any_${rootclassname}_representation"
    echo "#endif"
    echo "#line 1 \"${source_impl_file_base}\""
    cat "$source_impl_file" | sed -e "${impl_decl_lineno}"',$d'
    echo "#line "`expr 6 + ${impl_decl_lineno} + 1`" \"$newfile_base\""
    echo "#include \"${main_classname}.priv.h\""
    echo
    echo "const typeinfo_t ${main_classname}_typeinfo = { \"${main_classname}\" };"
    echo
    echo "static const typeinfo_t * const ${main_classname}_superclasses[] ="
    echo "  { ${main_classname}_SUPERCLASSES };"
    echo
    if test -n "${main_superclassname}"; then
      echo "#define super ${main_superclassname}_vtable"
      echo
    fi
    echo "#line "`expr $impl_end_lineno + 1`" \"${source_impl_file_base}\""
    cat "$source_impl_file" | sed -e "1,${impl_end_lineno}d" | sed -e "s,${main_classname}::,${main_classname}__,g"
    echo
    lineno=`wc -l < "$newfile"`
    echo "#line "`expr $lineno + 2`" \"$newfile_base\""
    # Define trivial stubs for methods that are not defined or overridden.
    inherited_method_names=`echo "$inherited_methods" | sed -e "$sed_remove_empty_lines" | sed -e "$sed_extract_method_name"`
    echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
      while read method; do
        rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
        name=`echo "$method" | sed -e "$sed_extract_method_name"`
        arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
        if test "$arglist" = "void"; then
          args=
        else
          args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
        fi
        if test "$rettype" = "void"; then
          return=
        else
          return="return "
        fi
        if cat "$source_impl_file" | sed -e "1,${impl_end_lineno}d" | sed -e "$sed_remove_comments" | grep "${main_classname}::${name} *(" > /dev/null; then
          # The class defines or overrides the method.
          :
        else
          # Add a stub for the method.
          inherited=
          for i in $inherited_method_names; do
            if test "$i" = "$name"; then
              inherited=yes
            fi
          done
          # First a prototype, to avoid gcc -Wmissing-prototypes warnings.
          echo "$rettype ${main_classname}__${name} ($arglist);"
          echo "$rettype"
          echo "${main_classname}__${name} ($arglist)"
          echo "{"
          if test -n "$inherited"; then
            echo "  ${return}super.${name} ($args);"
          else
            echo "  /* Abstract (unimplemented) method called.  */"
            echo "  abort ();"
            # Avoid C++ compiler warning about missing return value.
            echo "  #ifndef __GNUC__"
            echo "  ${return}${main_classname}__${name} ($args);"
            echo "  #endif"
          fi
          echo "}"
          echo
        fi
      done
    echo
    echo "const struct ${main_classname}_implementation ${main_classname}_vtable ="
    echo "{"
    echo "  ${main_classname}_superclasses,"
    echo "  sizeof (${main_classname}_superclasses) / sizeof (${main_classname}_superclasses[0]),"
    echo "  sizeof (struct ${main_classname}_representation),"
    echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
      while read method; do
        name=`echo "$method" | sed -e "$sed_extract_method_name"`
        echo "  ${main_classname}__${name},"
      done
    echo "};"
    echo
    echo "#if !HAVE_INLINE"
    echo
    echo "/* Define the functions that invoke the methods.  */"
    echo
    echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
      while read method; do
        rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
        name=`echo "$method" | sed -e "$sed_extract_method_name"`
        arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
        if test "$arglist" = "void"; then
          args=
        else
          args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
        fi
        if test "$rettype" = "void"; then
          return=
        else
          return="return "
        fi
        echo "$rettype"
        echo "${main_classname}_${name} ($arglist)"
        echo "{"
        echo "  const struct ${main_classname}_implementation *vtable ="
        echo "    ((struct ${main_classname}_representation_header *) (struct ${main_repclassalias} *) first_arg)->vtable;"
        echo "  ${return}vtable->${name} ($args);"
        echo "}"
        echo
      done
    echo "#endif"
  } > "$newfile"
}

# Generate the files in the source directory, not in the current directory.
# This is needed because they need to be distributed, since not all platforms
# have GNU 'sed' preinstalled.

sed_butbase='s,[^/]*$,,'
destdir=`echo "$source_impl_file" | sed -e "$sed_butbase"`

# Generate the source.h file first. The Makefile.am snippets rely on the
# fact that the other generated files have the same or a newer timestamp.
#
# Also, generate the source.c file last. The Makefile.am snippets don't know
# about the other generated files; they assume that when the source.c file
# is finished, this command is complete.

new_source_header_file_base=`echo "$source_header_file_base" | sed -e 's,\.oo\.h$,.h,'`
new_source_header_file="${destdir}$new_source_header_file_base"
func_start_creation "$new_source_header_file"
func_emit_source_h "$new_source_header_file" "$new_source_header_file_base" \
  || func_fatal_error "failed"

new_priv_header_file="${destdir}${main_classname}.priv.h"
func_start_creation "$new_priv_header_file"
func_emit_priv_h "$new_priv_header_file" \
  || func_fatal_error "failed"

new_vt_header_file="${destdir}${main_classname}.vt.h"
func_start_creation "$new_vt_header_file"
func_emit_vt_h "$new_vt_header_file" \
  || func_fatal_error "failed"

new_source_impl_file_base=`echo "$source_impl_file_base" | sed -e 's,\.oo\.c$,.c,'`
new_source_impl_file="${destdir}$new_source_impl_file_base"
func_start_creation "$new_source_impl_file"
func_emit_source_c "$new_source_impl_file" "$new_source_impl_file_base" \
  || func_fatal_error "failed"
